package com.bvbot.core.session;

import static com.bvbot.core.Logger.log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;

import com.bvbot.core.bot.UniqueTransactionIdGenerator;
import com.bvbot.core.tools.ExecutionMode;

public class BvSession {

	private static final String BV_URLS_SECURE_PREFIX = "https://live.bullionvault.com/secure/";

	private static final String BV_URLS_NOT_SECURE_PREFIX = "http://live.bullionvault.com/";

	private static final String BV_URL_LOGIN = BV_URLS_SECURE_PREFIX
			+ "j_security_check";

	private static final String BV_URL_VIEW_BALANCE = BV_URLS_SECURE_PREFIX
			+ "view_balance_xml.do";

	private static final String BV_URL_PLACE_ORDER = BV_URLS_SECURE_PREFIX
			+ "place_order_xml.do";

	private static final String BV_URL_VIEW_GOLD_MARKET_STATE = BV_URLS_NOT_SECURE_PREFIX
			+ "view_market_xml.do?considerationCurrency=USD&securityId=AUXZU&marketWidth=1&quantity=0.001";

	private static final String BV_URL_VIEW_SILVER_MARKET_STATE = BV_URLS_NOT_SECURE_PREFIX
			+ "view_market_xml.do?considerationCurrency=USD&securityId=AGXLN&marketWidth=1&quantity=0.001";

	private final String userName = "mrmoquette";
	private final String password = BvPassword.BV_PASSWORD;

	private HttpClient httpclient = new DefaultHttpClient();
	private boolean sessionStarted = false;
	private boolean isSessionOpened = false;

	private final UniqueTransactionIdGenerator generator = new UniqueTransactionIdGenerator();

	public synchronized void start() throws BvSessionException {
		if (sessionStarted) {
			throw new BvSessionException(
					"Session is already started. Please call stop to stop this session.");
		} else {
			sessionStarted = true;
		}
		log("Start of bv work session");
		String url = BV_URL_LOGIN + "?j_username=" + userName + "&j_password="
				+ password;
		HttpGet httpget = new HttpGet(url);

		HttpResponse response = null;
		StringBuilder content = new StringBuilder();
		try {
			response = httpclient.execute(httpget);
			HttpEntity entity = response.getEntity();
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					entity.getContent()));
			String inputLine;
			while ((inputLine = reader.readLine()) != null) {
				content.append(inputLine);
			}
		} catch (ClientProtocolException e) {
			log("Error while authenticating : " + e.getMessage(), e);
		} catch (IllegalStateException e) {
			log("Error while authenticating : " + e.getMessage(), e);
		} catch (IOException e) {
			log("Error while authenticating : " + e.getMessage(), e);
		}
		String responseContent = content.toString();
		log("Authentication Http status : " + response.getStatusLine());
		boolean isAuthentOk = true;
		if (response.getStatusLine().getStatusCode() != 200) {
			isAuthentOk = false;
			log("Bad http status code in return of authentication !");
		} else {
			if (responseContent.length() == 0
					|| responseContent.contains("failed")) {
				isAuthentOk = false;
				log("bad http content in return of authentication !");
			}
		}
		if (!isAuthentOk) {
			throw new BvSessionException(
					"Login failed. Please check your credentials");
		}
		isSessionOpened = true;
	}

	public synchronized void stop() throws BvSessionException {
		log("Stop of session : " + this);
		sessionStarted = false;
		isSessionOpened = false;
		httpclient = new DefaultHttpClient();
	}

	public BvResponse send(BvRequest request) {
		BvResponse result = null;
		return result;
	}

	public static void main(String[] args) throws BvSessionException {
		BvSession session = new BvSession();
		session.start();
		BalanceResponse response = session.getBalance();
		log("There is " + response.getGoldGrams() + " grams in balance");
		log(response.getContent());
		response = session.getBalance();
		log("There is " + response.getGoldGrams() + " grams in balance");
		log("Fin");
	}

	public BalanceResponse getBalance() throws BvSessionException {
		BasicBvRequest request = new BasicBvRequest(BV_URL_VIEW_BALANCE);
		String htmlContent = sendAuthentified(request);
		BalanceResponse result = new BalanceResponse(htmlContent);
		return result;
	}

	private String send(BasicBvRequest request, boolean connected)
			throws BvSessionException {
		String result = null;
		if (connected) {
			checkSessionOpened();
		}
		StringBuilder content = null;
		if (request != null) {
			try {
				HttpRequestBase httpMethod = null;
				if (request.isPost()) {
					httpMethod = new HttpPost(request.getUrl());
					List<NameValuePair> params = new ArrayList<NameValuePair>();
					request.addParams(params);
					((HttpPost) httpMethod).setEntity(new UrlEncodedFormEntity(
							params, HTTP.UTF_8));
				} else {
					httpMethod = new HttpGet(request.getUrl());
				}

				HttpResponse response = httpclient.execute(httpMethod);
				HttpEntity entity = response.getEntity();

				log("Response status for " + request.getUrl() + " : "
						+ response.getStatusLine());
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(entity.getContent()));
				String inputLine;
				content = new StringBuilder();
				while ((inputLine = reader.readLine()) != null) {
					content.append(inputLine);
				}
			} catch (ClientProtocolException e) {
				log("Error while sending " + request.getUrl() + " : "
						+ e.getMessage(), e);
			} catch (IllegalStateException e) {
				log("Error while sending " + request.getUrl() + " : "
						+ e.getMessage(), e);
			} catch (IOException e) {
				log("Error while sending " + request.getUrl() + " : "
						+ e.getMessage(), e);
			}
			if (content != null) {
				result = content.toString();
			}
		}
		return result;
	}

	private String sendAuthentified(BasicBvRequest request)
			throws BvSessionException {
		return send(request, true);
	}

	private String sendNotAuthentified(BasicBvRequest request)
			throws BvSessionException {
		return send(request, false);
	}

	private void checkSessionOpened() throws BvSessionException {
		if (!isSessionOpened) {
			throw new BvSessionException(
					"Session is in incorrect state : closed");
		}
	}

	public MarketResponse getGoldMarket() throws BvSessionException {
		BasicBvRequest request = new BasicBvRequest(
				BV_URL_VIEW_GOLD_MARKET_STATE);
		String htmlContent = sendNotAuthentified(request);
		MarketResponse result = new MarketResponse(htmlContent);
		return result;
	}

	public MarketResponse getSilverMarket() throws BvSessionException {
		BasicBvRequest request = new BasicBvRequest(
				BV_URL_VIEW_SILVER_MARKET_STATE);
		String htmlContent = sendNotAuthentified(request);
		MarketResponse result = new MarketResponse(htmlContent);
		return result;
	}

	public SaleResponse sell(BigDecimal sellPrice, int gramNumber,
			boolean isGold) throws BvSessionException {
		String security = isGold ? "gold" : "silver";
		String securityId = isGold ? "AUXZU" : "AGXLN";
		log("Sell of " + security + " asked at " + sellPrice + " for "
				+ gramNumber + " grams.");
		StringBuilder urlBuilder = new StringBuilder(BV_URL_PLACE_ORDER);
		BasicBvRequest request = new BasicBvRequest(urlBuilder.toString());
		request.setPost(true);
		request.addPostParameter("actionIndicator", "S");
		request.addPostParameter("considerationCurrency", "USD");
		request.addPostParameter("securityId", securityId);
		int numberOfKg = gramNumber / 1000;
		int numberOfGrams = gramNumber % 1000;
		String gramString = null;
		if (numberOfGrams < 10) {
			gramString = "00" + numberOfGrams;
		} else if (numberOfGrams < 100) {
			gramString = "0" + numberOfGrams;
		} else {
			gramString = "" + numberOfGrams;
		}
		request.addPostParameter("quantity", "" + numberOfKg + "." + gramString);
		log("Sell quantity : " + "" + numberOfKg + "." + gramString);
		request.addPostParameter("limit", "" + sellPrice.intValueExact());
		log("Sell limit : " + "" + sellPrice.intValueExact());
		request.addPostParameter("typeCode", "IMMEDIATE");
		request.addPostParameter("clientTransRef",
				generator.getNewUniqueTransactionId());
		request.addPostParameter("confirmed", "true");
		request.addPostParameter("goodUntil", "");
		log("Sending sale order for " + gramNumber + " gram(s) at " + sellPrice
				+ ". Request url is : " + urlBuilder.toString());
		String htmlContent = null;
		if (!ExecutionMode.isRealOrders()) {
			if (isGold) {
				htmlContent = "<envelope><message type=\"PLACE_ORDER_A\" version=\"0.1\"><order orderId=\"1080\" clientTransRef=\"asdf\" actionIndicator=\"B\" securityId=\"AUXZU\" considerationCurrency=\"USD\" quantity=\"0.001\" quantityMatched=\"0.001\" totalConsideration=\"12.59\" totalCommission=\"0.11\" limit=\"13500\" typeCode=\"TIL_CANCEL\" orderTime=\"2005-06-02 14:14:24 UTC\" goodUntil=\"\" lastModified=\"2005-06-02 14:14:25 UTC\" 	statusCode=\"DONE\"/></message></envelope>";
			} else {
				htmlContent = "<envelope><message type=\"PLACE_ORDER_A\" version=\"0.1\"><order orderId=\"1080\" clientTransRef=\"asdf\" actionIndicator=\"B\" securityId=\"AGXLN\" considerationCurrency=\"USD\" quantity=\"0.001\" quantityMatched=\"0.001\" totalConsideration=\"12.59\" totalCommission=\"0.11\" limit=\"13500\" typeCode=\"TIL_CANCEL\" orderTime=\"2005-06-02 14:14:24 UTC\" goodUntil=\"\" lastModified=\"2005-06-02 14:14:25 UTC\" 	statusCode=\"DONE\"/></message></envelope>";
			}
		} else {
			htmlContent = sendAuthentified(request);
			log("Received sell response : " + htmlContent);
		}
		SaleResponse response = new SaleResponse(htmlContent);
		return response;
	}

	public BuyResponse buy(BigDecimal buyPrice, int gramNumber, boolean isGold)
			throws BvSessionException {
		String security = isGold ? "gold" : "silver";
		String securityId = isGold ? "AUXZU" : "AGXLN";
		log("Buy of " + security + " asked at " + buyPrice + " for "
				+ gramNumber + " grams.");
		StringBuilder urlBuilder = new StringBuilder(BV_URL_PLACE_ORDER);
		BasicBvRequest request = new BasicBvRequest(urlBuilder.toString());
		request.setPost(true);
		request.addPostParameter("actionIndicator", "B");
		request.addPostParameter("considerationCurrency", "USD");
		request.addPostParameter("securityId", securityId);
		int numberOfKg = gramNumber / 1000;
		int numberOfGrams = gramNumber % 1000;
		String gramString = null;
		if (numberOfGrams < 10) {
			gramString = "00" + numberOfGrams;
		} else if (numberOfGrams < 100) {
			gramString = "0" + numberOfGrams;
		} else {
			gramString = "" + numberOfGrams;
		}
		request.addPostParameter("quantity", "" + numberOfKg + "." + gramString);
		log("Buy quantity : " + "" + numberOfKg + "." + gramString);
		request.addPostParameter("limit", "" + buyPrice.intValueExact());
		log("Buy limit : " + "" + buyPrice.intValueExact());
		request.addPostParameter("typeCode", "IMMEDIATE");
		request.addPostParameter("clientTransRef",
				generator.getNewUniqueTransactionId());
		request.addPostParameter("confirmed", "true");
		request.addPostParameter("goodUntil", "");
		log("Sending buy order for " + gramNumber + " gram(s) at " + buyPrice
				+ ". Request url is : " + urlBuilder.toString());
		String htmlContent = null;
		if (!ExecutionMode.isRealOrders()) {
			if (isGold) {
				htmlContent = "<envelope><message type=\"PLACE_ORDER_A\" version=\"0.1\"><order orderId=\"1080\" clientTransRef=\"asdf\" actionIndicator=\"B\" securityId=\"AUXZU\" considerationCurrency=\"USD\" quantity=\"0.001\" quantityMatched=\"0.001\" totalConsideration=\"12.59\" totalCommission=\"0.11\" limit=\"13500\" typeCode=\"TIL_CANCEL\" orderTime=\"2005-06-02 14:14:24 UTC\" goodUntil=\"\" lastModified=\"2005-06-02 14:14:25 UTC\" 	statusCode=\"DONE\"/></message></envelope>";
			} else {
				htmlContent = "<envelope><message type=\"PLACE_ORDER_A\" version=\"0.1\"><order orderId=\"1080\" clientTransRef=\"asdf\" actionIndicator=\"B\" securityId=\"AGXLN\" considerationCurrency=\"USD\" quantity=\"0.001\" quantityMatched=\"0.001\" totalConsideration=\"12.59\" totalCommission=\"0.11\" limit=\"13500\" typeCode=\"TIL_CANCEL\" orderTime=\"2005-06-02 14:14:24 UTC\" goodUntil=\"\" lastModified=\"2005-06-02 14:14:25 UTC\" 	statusCode=\"DONE\"/></message></envelope>";
			}
		} else {
			htmlContent = sendAuthentified(request);
			log("Received buy response : " + htmlContent);
		}
		BuyResponse response = new BuyResponse(htmlContent);
		return response;
	}
}
